from pycomm3 import CIPDriver
import struct
import time
import argparse

# devices = CIPDriver.discover() 
# print(devices)

class AlicatMFC():
    # 常量定义
    ASSEMBLY_CLASS = 0x04
    DATA_ATTRIBUTE = 0x03
    GET_ASSEMBLY_INSTANCE = 0x65  # Assembly实例101
    SET_ASSEMBLY_INSTANCE = 0x66  # Assembly实例102
    SET_ASSEMBLY_INSTANCE_FLOW = 0x64 # Assembly实例100
    GET_PARAMS_SERVICE = 0x0E
    SET_ATTRIBUTE_SERVICE = 0x10
    

    
    def __init__(self, ip_address=None):
        self.ip_address = ip_address       
        self.driver = None
        self.is_connected = False
        
    def discover_devices(self):
        """搜索网络中的CIP设备
        Returns:
            list: 发现的设备列表，每个元素是设备信息字典
        """      
        try:
            print("正在搜索网络中的EIP设备...")
            devices = CIPDriver.discover()  # pycomm3的设备发现功能
            # time.sleep(0.5)
            
            if not devices:
                print("未发现任何EIP设备")
                return []
                
            print(f"发现{len(devices)}个EIP设备:")
            for i, dev in enumerate(devices, 1):
                print(f"{i}. IP地址: {dev['ip_address']}, 设备名称: {dev.get('product_name', '未知')}")
                
            return devices
            
        except Exception as e:
            print(f"设备搜索失败: {e}")
            return []
     
    def connect(self, ip_address=None):
        # 确定要连接的IP地址
        target_ip = ip_address or self.ip_address
        
        # 如果仍未确定IP，尝试自动发现
        if not target_ip:
            devices = self.discover_devices()
            if devices:
                target_ip = devices[0]['ip_address']
                # print(f"将连接到第一个发现的设备: {target_ip}")
            else:
                print("无法确定要连接的设备IP地址")
                return False
        # 尝试连接设备
        try:
            self.driver = CIPDriver(target_ip)
            self.driver.open()
            self.ip_address = target_ip  # 更新实例的IP地址
            print(f"成功连接到设备: {target_ip}")
            self.is_connected = True
            return True
        except Exception as e:
            print(f'连接设备失败: {e}')
            return False
        
    def disconnect(self):
        # 断开设备
        if self.driver:
            self.driver.close()
            self.driver = None
            self.is_connected = False
            print(f"已断开与 {self.ip_address} 的连接")
            
    def get_basic_params(self):
        """获取MFC基本参数
        气体索引: int;
        绝对压力: float;
        温度: float;
        体积流速: float;
        质量流速: float;
        流速设定值: float;
        累积流速: float;
        """
#         if not self.is_connected or not self.driver:
        if not self.driver:
            self.connect()
        try:
            response = self.driver.generic_message(
                service= self.GET_PARAMS_SERVICE,
                class_code= self.ASSEMBLY_CLASS,  # Assembly类
                instance= self.GET_ASSEMBLY_INSTANCE,     # Assembly实例101
                attribute= self.DATA_ATTRIBUTE,      # Data属性
                request_data=struct.pack('HH', 82, 4),
                connected=False 
            )
            response_setpoint = self.driver.generic_message(
                service= self.GET_PARAMS_SERVICE,
                class_code= self.ASSEMBLY_CLASS,  # Assembly类
                instance= 0x6B,     # Assembly实例101
                attribute= self.DATA_ATTRIBUTE,      # Data属性
                request_data=struct.pack('HH', 48, 4),
                connected=False 
            )
            if response.error:
                print(f"Error: {response.error}")
                return None
            # 解析响应数据
            response_bytes = bytes(response.value)
            response_bytes_setpoint = bytes(response_setpoint.value)
            # 气体类型
            gas_number = struct.unpack('h', response_bytes[:2])[0]
            # 绝压
            abs_pressure = struct.unpack('f', response_bytes[6:10])[0]
            # 温度
            temp = struct.unpack('f', response_bytes[10:14])[0]
            # 体积流速
            volumetric_flow = struct.unpack('f', response_bytes[14:18])[0]
            # 质量流速
            mass_flow = struct.unpack('f', response_bytes[18:22])[0]
            # 当前流速设定值
            setpoint = struct.unpack('f', response_bytes_setpoint[8:12])[0]
            # 累计流量
            totalizer = struct.unpack('f', response_bytes_setpoint[40:44])[0]
            # 构建返回参数
            params = {
                'gas_number': gas_number,
                'abs_pressure': abs_pressure,
                'temperature': temp,
                'volumetric_flow': volumetric_flow,
                'mass_flow': mass_flow,
                'setpoint': setpoint,
                'totalizer': totalizer
            }
             # 打印参数
#             self._print_params(params)
#             self.driver.close()
            print(params)
            return params
        
        except Exception as e:
            print(f"获取参数时异常: {e}")
            return None
            
        
    def set_gas(self, gas_number):
        """设置气体类型
        
        Args:
            gas_number (int): 气体编号
            
        Returns:
            bool: 设置成功返回True，否则返回False
        """
        if not self.is_connected or not self.driver:
            self.connect()
            
        try:
            response = self.driver.generic_message(
                service=self.SET_ATTRIBUTE_SERVICE,
                class_code=self.ASSEMBLY_CLASS,
                instance=self.SET_ASSEMBLY_INSTANCE,
                attribute=self.DATA_ATTRIBUTE,
                request_data=struct.pack('HH', 1, gas_number),
                connected=True
            )
            
            if response.error:
                print(f"设置气体错误: {response.error}")
                return False
            else:
                print(f"气体修改成功......, 当前气体索引为{gas_number}")
#                 self.driver.close()
                return True
                
        except Exception as e:
            print(f"设置气体时发生异常: {e}")
            return False
        
    def set_flowrate(self, setpoint):
        """设置质量流速
        
        Args:
            flowrate (float): 质量流速
            
        Returns:
            bool: 设置成功返回True，否则返回False
        """
        if not self.is_connected or not self.driver:
            self.connect()
 
        try:
            response = self.driver.generic_message(
                service=self.SET_ATTRIBUTE_SERVICE,
                class_code=self.ASSEMBLY_CLASS,
                instance=self.SET_ASSEMBLY_INSTANCE_FLOW,
                attribute=self.DATA_ATTRIBUTE,
                request_data=struct.pack('f', setpoint),
                connected=True
            )
            
            if response.error:
                print(f"设置流速错误: {response.error}")
                self.driver.close()
                return False
            else:
                print(f"流速设置成功......, 当前流速设定值为{setpoint}")
                return True
                
        except Exception as e:
            print(f"设置流速时发生异常: {e}")
            return False


def main():
    # 解析命令行参数
    parser = argparse.ArgumentParser(description='EIP设备通信工具（支持自动发现设备）')
    parser.add_argument('--ip', help='设备IP地址，不指定则自动搜索')
    subparsers = parser.add_subparsers(dest='command', help='命令')
    
    # 读取参数命令
    read_parser = subparsers.add_parser('read', help='读取设备参数')
    
    # 设置气体命令
    set_parser = subparsers.add_parser('set_gas', help='设置气体')
    set_parser.add_argument('gas_number', type=int, help='气体编号')
    
    # 设置流速命令
    set_parser = subparsers.add_parser('set_flowrate', help='设置流速')
    set_parser.add_argument('setpoint', type=float, help='流速大小')
    
    # 仅搜索设备命令
    discover_parser = subparsers.add_parser('discover', help='仅搜索网络中的EIP设备')
    
    args = parser.parse_args()
    
    # 初始化设备通信器
    device = AlicatMFC(args.ip)
    
    try:
        # 如果只是搜索设备，完成后直接退出
        if args.command == 'discover':
            device.discover_devices()
            return           
        # 根据命令执行相应操作
        elif args.command == 'read':
            device.get_basic_params()
        elif args.command == 'set_gas':
            device.set_gas(args.gas_number)            
        elif args.command == 'set_flowrate':
            device.set_flowrate(args.setpoint)

            
#     finally:
        # 确保断开连接
#         device.disconnect()
    except:
        return
    
# alicat = AlicatMFC()
# alicat.set_flowrate(0)
if __name__ == "__main__":
    main()
    